package com.ctyun.xstore.vod;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ctyun.xstore.vod.exception.ServerErrorException;
import com.ctyun.xstore.vod.internal.HttpStatus;
import com.ctyun.xstore.vod.internal.OkHttpClientBuilder;
import com.ctyun.xstore.vod.video.model.PartETag;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public abstract class AbstractPartUploader {

	private final Logger log = LoggerFactory.getLogger(AbstractPartUploader.class);

	private static final String ETAG = "etag";

	private static final int UPLOAD_RETRY_TIMES = 4;

	private final OkHttpClient httpClient = OkHttpClientBuilder.singaletonClient();

	protected abstract List<PartETag> doMultiPartupload(File file, List<String> urls, int partSize) throws IOException;

	/**
	 * 分片上传
	 * 
	 * @param file
	 *            The file to be uploaded.
	 * @param urls
	 *            A list of s3 pre-signed urls
	 * @param partSize
	 *            How many bytes will be in a part to upload.
	 * @throws IOException
	 *             there may be an IOExcption throwing out.
	 */
	public List<PartETag> multiPartupload(File file, List<String> urls, int partSize) throws IOException {
		if (file == null) {
			throw new IllegalArgumentException("Cannot read file: file is Null");
		}
		if (!file.exists() || !file.isFile()) {
			throw new IllegalArgumentException("Cannot read file:" + file.getAbsolutePath());
		}
		if (partSize <= 0) {
			throw new IllegalArgumentException("parSize must be positiveInteger!");
		}
		return doMultiPartupload(file, urls, partSize);
	}
	
	/**
	 * upload a file to url.
	 * 
	 * @param url
	 *            the s3 presigned url.
	 * @param file
	 *            the file to upload
	 * @return etag at the response headers
	 * @throws IOException
	 *             there may be an ioexcption throwing out.
	 */
	public String upload(String url, File file) throws IOException {
		return this.doUpload(url, RequestBody.create(file, null));
	}
	
	/**
	 * upload parts of the file to url.
	 * 
	 * @param url
	 *            a s3 presigned url
	 * @param content
	 *            part data
	 * @throws IOException
	 *             there may be an ioexcption throwing out.
	 */
	public String upload(String url, byte[] content) throws IOException {
		return this.doUpload(url, RequestBody.create(content, null));
	}
	
	/**
	 * upload parts of the file to url.
	 * 
	 * @param url
	 *            a s3 presigned url
	 * @param content
	 *            part data
	 * @param offset
	 *            offset
	 * @param byteCount
	 *            byte count
	 * @return etag at the response headers
	 * @throws IOException
	 *             there may be an ioexcption throwing out.
	 */
	public String upload(String url, final byte[] content, final int offset, final int byteCount) throws IOException {
		return this.doUpload(url, RequestBody.create(content, null, offset, byteCount));
	}
	
	private String doUpload(String url, RequestBody body) throws IOException {
		Request req = new Request.Builder().url(url).put(body).build();
		int uploadRetryCount = 0;
		String resBodyStr = "";
		int httpCode = HttpStatus.CODE_500;
		while (uploadRetryCount < UPLOAD_RETRY_TIMES) {
			try (Response res = httpClient.newCall(req).execute()) {
				resBodyStr = res.body().string();
				httpCode = res.code();
				if (res.isSuccessful()) {
					log.info("Put part to [{}] successfully!", url);
					return res.header(ETAG);
				}
				log.error("put part to [{}] failed,failed code is {}, error:{}", url, httpCode, resBodyStr);
				uploadRetryCount++;
				log.info("Retry to put part to [{}] again!", url);
			} catch (Exception ex) {
				log.error("put part to [{}] failed:", url, ex);
				uploadRetryCount++;
				log.info("Retry to put part to [{}] again!", url);
			}
		}
		throw new ServerErrorException(httpCode, "上传发生错误返回异常数据 %s", resBodyStr);
	}
}
